using System;
using System.Collections.Generic;
using System.Linq;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Companies;
using Sustainalytics.Entities;
using Sustainalytics.Portfolios.BL.Model;
using Sustainalytics.Utils;
using MongoDB.Driver;
using System.Diagnostics;

namespace Sustainalytics.Portfolios.BL
{
    public class PortfolioMatching : IPortfolioMatching
    {
        private readonly ICompanyMatchingLogic _companyMachingLogic;
        private readonly CorporateDataUniversesLoader _corporateDataCollection;
        private EventLog _eventLog;


        public PortfolioMatching(ICompanyMatchingLogic companyMachingLogic, CorporateDataUniversesLoader mongoCollection)
        {
            _companyMachingLogic = companyMachingLogic;
            _corporateDataCollection = mongoCollection;

            this._eventLog = new EventLog("Application");
            this._eventLog.Source = "PortfolioMatching";
        }

        public IList<PortfolioSecuritiesRow> GetMainSecurityForCompanies(ICollection<int> capitalIqIds, PortfolioUserClaims claims)
        {
            List<PortfolioSecuritiesRow> results = new List<PortfolioSecuritiesRow>();

            var securitiesGrouping = _companyMachingLogic.GetCompaniesSearchByCompanyId(capitalIqIds).GroupBy(x=>x.CompanyId);

            _corporateDataCollection.SetCompaniesUniversesDictionary(capitalIqIds);

            var policy = new MatchingSecuritiesAlgorithm();

            foreach (var securityGroup in securitiesGrouping)
            {
                MatchingResult matchingResult = policy.GetSecurity(securityGroup.ToList());

                if (matchingResult.MatchType == PortfolioMatchType.CompanyHasNoSecurity ||
                    matchingResult.MatchType == PortfolioMatchType.CompanyHasNoValidSecurity ||
                    matchingResult.MatchType == PortfolioMatchType.SecuritiesMatchDifferentCompanies ||
                    matchingResult.MatchType == PortfolioMatchType.NoMatch)
                    continue;

                var match = matchingResult.Matches.FirstOrDefault();

                var companyUniverses = _corporateDataCollection.GetCompanyUniverses(match.CompanyId);
                var securityRow = CreateSecurityRowInfo(match, companyUniverses);
                if (claims != null)
                    securityRow.Security.ValidateAgainstUserUniverses(claims.Universes);
                results.Add(securityRow);

            }
            return results;
        }

        public IList<PortfolioSecuritiesRow> GetMainSecurityForCompany(int capitalIqId, PortfolioUserClaims claims)
        {
       
            var securities= _companyMachingLogic.GetCompanySearchByCompanyId(capitalIqId);
            _corporateDataCollection.SetCompaniesUniversesDictionary(new[] { capitalIqId });

            List<PortfolioSecuritiesRow> results = new List<PortfolioSecuritiesRow>();

            var policy = new MatchingCompanySecuritiesAlgorithm();
            MatchingResult matchingResult = policy.GetSecurity(securities);
            if (!matchingResult.IsValid)
            {
                PortfolioManagementEventSource.Log.PotfolioInfo(string.Format("Company with capitalIqId {0} not matched because {1}", capitalIqId, matchingResult.MatchDetails));
            }

            if (matchingResult.MatchType == PortfolioMatchType.ExactMatch ||
                matchingResult.MatchType == PortfolioMatchType.SecuritiesMatchSameCompany ||
                matchingResult.MatchType == PortfolioMatchType.CompanyHasNoSecurity)
            {
                var match = matchingResult.Matches.FirstOrDefault();

                var companyUniverses = _corporateDataCollection.GetCompanyUniverses(capitalIqId);
                var securityRow = CreateSecurityRowInfo(match, companyUniverses);

                securityRow.Security.ValidateAgainstUserUniverses(claims.Universes);
                results.Add(securityRow);
            }

            return results;
        }

        public IList<PortfolioMatches> GetSecurityRowForIdentifier(string securityName, string securityIdentifier, PortfolioUserClaims claims)
        {
            
            try
            {
                List<Company> listSecurities = _companyMachingLogic.MatchCompanies(0, 0, securityIdentifier.ToLowerInvariant(), false);
                List <PortfolioMatches> results = new List<PortfolioMatches>();

                var policy = new MatchingSecuritiesAlgorithm();
                MatchingResult matchingResult = policy.GetSecurity(listSecurities);

                if (!matchingResult.IsValid)
                {
                    PortfolioManagementEventSource.Log.PotfolioInfo(string.Format("Security with identifier {0} not matched because {1}", securityIdentifier, matchingResult.MatchDetails));
                }

                if (matchingResult.MatchType != PortfolioMatchType.NoMatch && matchingResult.Matches.Any())
                {
                    var groupId = Guid.NewGuid();
                    var companyIds = matchingResult.Matches.Select(x => x.CompanyId).ToList();
                    // store universeDictionary in local variable to ensure thread safety
                    var universeDictionary = _corporateDataCollection.GetCompaniesUniversesDictionary(companyIds);

                    foreach (var match in matchingResult.Matches)
                    {
                        //use local universeDictionary to ensure thread safety  
                        var companyUniverses = universeDictionary.ContainsKey(match.CompanyId) ? universeDictionary[match.CompanyId] : new List<int>();
                        var portfolioSecurityRow = CreatePortfolioSecurityRowInfo(securityName, securityIdentifier, match, companyUniverses, matchingResult.MatchDetails, matchingResult.IsValid);
                        portfolioSecurityRow.Security.ValidateAgainstUserUniverses(claims.Universes);
                        var portfolioMatch = CreatePortfolioMatch(securityName, securityIdentifier, match, matchingResult, portfolioSecurityRow.Security.IsAccesible, groupId);

                        results.Add(portfolioMatch);
                    }
                }
                else
                {
                    var groupId = Guid.NewGuid();
                    var portfolioMatch = CreatePortfolioMatch(securityName, securityIdentifier, null, matchingResult, false, groupId);
                    results.Add(portfolioMatch);
                }

                return results;
            }
            catch(Exception exc)
            {
                this._eventLog.WriteEntry("GetSecurityRowForIdentifier "+ exc.Message);
                throw;
            }
            
        }

        private PortfolioSecuritiesRow CreateSecurityRowInfo(Company companyMatch, ICollection<int> universeIds)
        {
            PortfolioSecurityModel detail;
            bool isValid;
            if (companyMatch != null)
            {
                detail = new PortfolioSecurityModel(
                    Guid.NewGuid(),
                    Guid.Empty,
                    new DisplayInformation(companyMatch.CompanyId.ToString(), string.Empty),
                    new MatchedInformation(companyMatch.CompanyId.ToString(), companyMatch.CompanyName),
                    _companyMachingLogic.FindGuidForOneCompany(companyMatch),
                    _companyMachingLogic.FindCompanyNameFriendlyURLForId(companyMatch.CompanyId),
                    _companyMachingLogic.FindSecurityIdForCompanyId(companyMatch.CompanyId),
                    - 1);
                detail.AttachFeatureUniverses(universeIds);
                isValid = true;
            }
            else
            {
                detail = new PortfolioSecurityModel.Null();
                isValid = false;
            }
            return new PortfolioSecuritiesRow(detail, string.Empty, isValid);
        }

        private PortfolioMatches CreatePortfolioMatch(string securityName, string securityIdentifier, Company match, MatchingResult matchResult, bool hasAccess, Guid groupId)
        {
            if (matchResult.MatchType == PortfolioMatchType.CompanyHasNoSecurity || matchResult.MatchType == PortfolioMatchType.NoMatch)
            {
                return new PortfolioMatches
                {
                    Id = Guid.NewGuid(),
                    PortfolioId = Guid.Empty,
                    DisplayInfo = new DisplayInformation(securityIdentifier, securityName),
                    MatchedInfo = new MatchedInformation(),
                    IsValid = false,
                    Added = false,
                    MatchType = PortfolioMatchType.CompanyHasNoSecurity,
                    GroupId = groupId,
                    Created = DateTime.UtcNow
                };
            }

            return new PortfolioMatches
            {
                Id = Guid.NewGuid(),
                PortfolioId = Guid.Empty,
                DisplayInfo = new DisplayInformation(securityIdentifier, securityName),
                MatchedInfo = new MatchedInformation(match.CompanyId.ToString(), match.CompanyName),
                CompanyId = _companyMachingLogic.FindGuidForOneCompany(match),
                IsValid = matchResult.IsValid,
                Added = matchResult.IsValid,
                MatchType = !hasAccess ? PortfolioMatchType.NoAccess : matchResult.MatchType,
                GroupId = groupId,
                Created = DateTime.UtcNow
            };
        }
        private PortfolioSecuritiesRow CreatePortfolioSecurityRowInfo(
            string securityName,
            string securityIdentifier,
            Company companySearch,
            ICollection<int> universeIds,
            string matchDetails,
            bool isValid)
        {
            PortfolioSecurityModel detail;
            if (companySearch != null)
            {
                detail = new PortfolioSecurityModel(
                   Guid.NewGuid(),
                   Guid.Empty,
                   new DisplayInformation(securityIdentifier, securityName),
                   new MatchedInformation(companySearch.CompanyId.ToString(), companySearch.CompanyName),
                   _companyMachingLogic.FindGuidForOneCompany(companySearch),
                   _companyMachingLogic.FindCompanyNameFriendlyURLForId(companySearch.CompanyId),
                   _companyMachingLogic.FindSecurityIdForCompanyId(companySearch.CompanyId),
                   - 1);

                detail.AttachFeatureUniverses(universeIds);
            }
            else
            {
                detail = new PortfolioSecurityModel.Null(Guid.NewGuid(), Guid.Empty, securityName, securityIdentifier);
            }

            return new PortfolioSecuritiesRow(detail, matchDetails, isValid);
        }
    }
}